﻿namespace CCS.DAL.Repository
{
    using System;
    using System.Collections.Generic;
    using System.Data.Objects;
    using System.Linq;

    using CCS.DAL.Model;

    using CIK.Framework.Diagnostics;

    public abstract class RepositoryBase<TEntity> : IRepository<TEntity> where TEntity : class
    {
        public ChiChymShopEntities DataContext { get; private set; }

        public IObjectSet<TEntity> ObjectSet { get; private set; }

        protected RepositoryBase()
            : this(new ChiChymShopEntities())
        {
        }

        protected RepositoryBase(ChiChymShopEntities dataContext)
        {
            this.DataContext = dataContext;
            this.ObjectSet = this.DataContext.CreateObjectSet<TEntity>();
        }

        public IQueryable<TEntity> Fetch()
        {
            this.ThrowExceptionIfViolateRule();

            return this.ObjectSet;
        }

        public IEnumerable<TEntity> GetAll()
        {
            this.ThrowExceptionIfViolateRule();

            return this.ObjectSet.AsEnumerable();
        }

        public IEnumerable<TEntity> Find(Func<TEntity, bool> predicate)
        {
            this.ThrowExceptionIfViolateRule();

            return this.ObjectSet.Where(predicate);
        }

        public TEntity Single(Func<TEntity, bool> predicate)
        {
            this.ThrowExceptionIfViolateRule();

            return this.ObjectSet.Single(predicate);
        }

        public TEntity First(Func<TEntity, bool> predicate)
        {
            this.ThrowExceptionIfViolateRule();

            return this.ObjectSet.First(predicate);
        }

        public void Delete(TEntity entity)
        {
            this.ThrowExceptionIfViolateRule();

            if (entity == null)
            {
                throw new ArgumentNullException("entity");
            }

            this.ObjectSet.DeleteObject(entity);
        }

        public void Delete(Func<TEntity, bool> predicate)
        {
            this.ThrowExceptionIfViolateRule();

            var records = from x in this.ObjectSet.Where(predicate) select x;

            foreach (var record in records)
            {
                this.ObjectSet.DeleteObject(record);
            }
        }

        public void Add(TEntity entity)
        {
            this.ThrowExceptionIfViolateRule();

            if (entity == null)
            {
                throw new ArgumentNullException("entity");
            }

            this.ObjectSet.AddObject(entity);
        }

        public void Attach(TEntity entity)
        {
            this.ThrowExceptionIfViolateRule();

            this.ObjectSet.Attach(entity);
        }

        public void SaveChanges()
        {
            this.ThrowExceptionIfViolateRule();

            this.DataContext.SaveChanges();
        }

        public void SaveChanges(SaveOptions options)
        {
            this.ThrowExceptionIfViolateRule();

            this.DataContext.SaveChanges(options);
        }

        public void Dispose()
        {
            this.Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (this.DataContext != null)
                {
                    this.DataContext.Dispose();
                    this.DataContext = null;
                }
            }
        }

        private void ThrowExceptionIfViolateRule()
        {
            Throw.IfArgumentNull(this.DataContext, "DataContext is null or empty");
            Throw.IfArgumentNull(this.ObjectSet, "ObjectSet is null or empty");
        }
    }
}